# coding: utf8

import access
import util

DEFAULT_GRADING_INSTRUCTIONS = """
Use the following grading scale:
- 10: Awesome
- 9:
- 8:
- 7:
- 6: Meets expectations.
- 5: 
- 4: 
- 3: 
- 2: Minimal effort in the right direction.
- 1: The submission is present, but there is essentially no work in it.
- 0: Missing submission, no work done, or wrong material submitted.
When grading, please consider:
- Clarity
- Functionality
- Style
"""


@auth.requires_login()
def view_venue():
    c = db.venue(request.args(0)) or redirect(URL('default', 'index'))
    props = db(db.user_properties.user == auth.user.email).select().first()
    # if c.raters_equal_submitters:
    db.venue.rate_constraint.readable = False
    if props == None: 
        can_submit = False
        can_rate = False
        has_submitted = False
        has_rated = False
        can_manage = False
        can_observe = False
        can_view_ratings = False
    else:
        can_submit = c.id in util.get_list(props.venues_can_submit)
        can_rate = c.id in util.get_list(props.venues_can_rate)
        has_submitted = c.id in util.get_list(props.venues_has_submitted)
        has_rated = c.id in util.get_list(props.venues_has_rated)
        can_manage = c.id in util.get_list(props.venues_can_manage)
        can_observe = c.id in util.get_list(props.venues_can_observe)
        # MAYDO(luca): Add option to allow only raters, or only submitters, to view
        # all ratings.
        can_view_ratings = access.can_view_ratings(c, props)
    if access.can_observe(c, props):
        db.venue.grading_instructions.readable = True
    venue_form = SQLFORM(db.venue, record=c, readonly=True)
    link_list = []
    if can_manage:
        link_list.append(A(T('Edit'), _href=URL('venues', 'edit', args=[c.id], user_signature=True)))
    if can_submit:
        link_list.append(A(T('Submit to this venue'), _href=URL('submission', 'submit', args=[c.id])))
    if can_manage:
        link_list.append(A(T('Add submission'), _href=URL('submission', 'manager_submit', args=[c.id])))
    if has_submitted:
        link_list.append(A(T('View my submissions'), _href=URL('feedback', 'index', args=[c.id])))
    if can_view_ratings or access.can_view_submissions(c, props):
        link_list.append(A(T('View submissions'), _href=URL('ranking', 'view_submissions', args=[c.id])))
    if can_rate:
        link_list.append(A(T('Review a submission'), _href=URL('rating', 'accept_review', args=[c.id], user_signature=True)))
    if can_observe or can_manage:
        link_list.append(A(T('View comparisons'), _href=URL('ranking', 'view_comparisons_index', args=[c.id])))
    if can_manage:
        link_list.append(A(T('Run reputation system'), _href=URL('rating', 'run_rep_system', args=[c.id])))
    if can_view_ratings:
        link_list.append(A(T('View grades'), _href=URL('ranking', 'view_final_grades', args=[c.id])))
    if auth.user.email in ['luca@ucsc.edu', 'mshavlov@ucsc.edu']:
        link_list.append(A(T('Research view'), _href=URL('view_venue_research', args=[c.id])))
    return dict(form=venue_form, link_list=link_list, venue=c, has_rated=has_rated)
        

@auth.requires_login()
def view_venue_research():
    c = db.venue(request.args(0)) or redirect(URL('default', 'index'))
    props = db(db.user_properties.user == auth.user.email).select().first()
    if props == None: 
        can_submit = False
        can_rate = False
        has_submitted = False
        has_rated = False
        can_manage = False
        can_observe = False
        can_view_ratings = False
    else:
        can_submit = c.id in util.get_list(props.venues_can_submit) or util.is_none(c.submit_constraint)
        can_rate = c.id in util.get_list(props.venues_can_rate) or util.is_none(c.rate_constraint)
        has_submitted = c.id in util.get_list(props.venues_has_submitted)
        has_rated = c.id in util.get_list(props.venues_has_rated)
        can_manage = c.id in util.get_list(props.venues_can_manage)
        can_observe = c.id in util.get_list(props.venues_can_observe)
        # MAYDO(luca): Add option to allow only raters, or only submitters, to view
        # all ratings.
        can_view_ratings = access.can_view_ratings(c, props)
    db.venue.ranking_algo_description.readable = True
    venue_form = SQLFORM(db.venue, record=c, readonly=True)
    link_list = []
    if can_submit:
        link_list.append(A(T('Submit to this assignment'), _href=URL('submission', 'submit', args=[c.id])))
    if can_rate:
        link_list.append(A(T('Review a submission'), _href=URL('rating', 'accept_review', args=[c.id])))
    if has_submitted:
        link_list.append(A(T('View my submissions'), _href=URL('feedback', 'index', args=[c.id])))
    if can_manage:
        link_list.append(A(T('Edit'), _href=URL('managed_index', vars=dict(cid=c.id))))
        link_list.append(A(T('Add submission'), _href=URL('submission', 'manager_submit', args=[c.id])))
        link_list.append(A(T('Rep sys - small alpha'), _href=URL('rating', 'run_rep_sys_research', args=[c.id, 2])))
        link_list.append(A(T('Ranking without rep sys'), _href=URL('rating', 'run_rep_sys_research', args=[c.id, 1])))
    if can_observe or can_manage:
        link_list.append(A(T('View comparisons'), _href=URL('ranking', 'view_comparisons_index', args=[c.id])))
    if can_view_ratings or access.can_view_submissions(c, props):
        link_list.append(A(T('View submissions'), _href=URL('ranking', 'view_submissions', args=[c.id])))
    if access.can_view_rating_contributions(c, props):
        link_list.append(A(T('View reviewer contribution'), _href=URL('ranking', 'view_raters', args=[c.id])))
    if can_view_ratings:
        link_list.append(A(T('View final grades'), _href=URL('ranking', 'view_final_grades', args=[c.id])))
    return dict(form=venue_form, link_list=link_list, venue=c, has_rated=has_rated)


@auth.requires_login()
def subopen_index():
    props = db(db.user_properties.user == auth.user.email).select(db.user_properties.venues_can_submit).first()
    if props == None: 
        l = []
    else:
        l = util.get_list(props.venues_can_submit)
    t = datetime.utcnow()
    if len(l) == 0:
        q = (db.venue.id == -1)
    else:
        q = ((db.venue.close_date > t) & (db.venue.open_date < t) &
             (db.venue.is_active == True) & (db.venue.id.belongs(l)))
    grid = SQLFORM.grid(q,
        field_id=db.venue.id,
        fields=[db.venue.name, db.venue.open_date, db.venue.close_date],
        csv=False, details=False, create=False, editable=False, deletable=False,
        links=[
            dict(header=T('Submit'), body = lambda r: submit_link(r)),
            ],
        maxtextlength=24,
        )
    return dict(grid=grid)

def submit_link(r):
    if r.open_date > datetime.utcnow():
        return T('Not yet open')
    else:
        return A(T('Submit'), _class='btn', _href=URL('submission', 'submit', args=[r.id]))


@auth.requires_login()
def rateopen_index():
    #TODO(luca): see if I can put an inline form for accepting review tasks.
    props = db(db.user_properties.user == auth.user.email).select().first()
    if props == None:
        l = []
    else:
        l = util.get_list(props.venues_can_rate)
    t = datetime.utcnow()
    if len(l) == 0:
        q = (db.venue.id == -1)
    else:
        q = ((db.venue.rate_open_date < t) & (db.venue.rate_close_date > t) &
             (db.venue.is_active == True) & (db.venue.is_approved == True) & (db.venue.id.belongs(l)))
    db.venue.rate_close_date.label = T('Review deadline')
    grid = SQLFORM.grid(q,
        field_id=db.venue.id,
        fields=[db.venue.name, db.venue.rate_open_date, db.venue.rate_close_date],
        csv=False, details=False, create=False, editable=False, deletable=False,
        links=[
            dict(header=T('Review'), body = lambda r: review_link(r)),
            ],
        maxtextlength=24,
        )
    return dict(grid=grid)

def review_link(r):
    if r.rate_open_date > datetime.utcnow():
        return T('Reviewing is not yet open')
    else:
        return A(T('Accept reviewing task'), _class='btn',
                 _href=URL('rating', 'accept_review', args=[r.id], user_signature=True))


@auth.requires_login()
def observed_index():
    props = db(db.user_properties.user == auth.user.email).select().first()    
    if props == None: 
        l = []
    else:
        l = util.id_list(util.get_list(props.venues_can_observe))
        l1 = util.id_list(util.get_list(props.venues_can_manage))
        for el in l1:
            if el not in l:
                l.append(el)
    if len(l) > 0:
        q = (db.venue.id.belongs(l) & (db.venue.is_approved == True))
    else:
        q = (db.venue.id == -1)
    db.venue.number_of_submissions_per_reviewer.readable = True
    db.venue.grading_instructions.readable = True
    db.venue.is_approved.readable = True
    grid = SQLFORM.grid(q,
        field_id=db.venue.id,
        fields=[db.venue.name, db.venue.close_date, db.venue.rate_close_date],
        csv=False, details=True, create=False, editable=False, deletable=False,
        maxtextlength=24,
        )
    return dict(grid=grid)

                
@auth.requires_login()
def reviewing_duties():
    """This function lists venues where users have reviews to accept, so that users
    can be redirected to a page where to perform such reviews."""
    # Produces a list of venues that are open for rating.
    props = db(db.user_properties.user == auth.user.email).select(db.user_properties.venues_can_rate).first()
    if props == None:
        l = []
    else:
        l = util.get_list(props.venues_can_rate)
    t = datetime.utcnow()
    if len(l) == 0:
        q = (db.venue.id == -1)
    else:
        q = ((db.venue.rate_open_date < t) & (db.venue.rate_close_date > t)
             & (db.venue.is_active == True) & (db.venue.id.belongs(l)))
    db.venue.rate_close_date.label = T('Review deadline')
    db.venue.number_of_submissions_per_reviewer.label = T('Total n. of reviews')
    grid = SQLFORM.grid(q,
        field_id=db.venue.id,
        fields=[db.venue.name, db.venue.rate_open_date, db.venue.rate_close_date,
                db.venue.number_of_submissions_per_reviewer],
        csv=False, details=False, create=False, editable=False, deletable=False,
        links=[
            dict(header=T('N. reviews still to do'), body = lambda r: get_num_reviews_todo(r)),
            dict(header='Accept', body = lambda r: review_link(r)),
            ],
        maxtextlength=24,
        )
    return dict(grid=grid)


def get_num_reviews_todo(venue):
    if venue.number_of_submissions_per_reviewer == 0 or venue.number_of_submissions_per_reviewer == None:
        return 0
    # See how many reviewing tasks the user has accepted.
    n_accepted_tasks = db((db.task.venue_id == venue.id) &
                          (db.task.user == auth.user.email)).count()
    return max(0, venue.number_of_submissions_per_reviewer - n_accepted_tasks)


def view_venue_link(venue_id):
    v = db.venue(venue_id)
    if v == None:
        return ''
    return A(v.name, _href=URL('venues', 'view_venue', args=[venue_id]))


def get_review_deadline(venue_id):
    v = db.venue(venue_id)
    if v == None:
        return ''
    return v.rate_close_date


@auth.requires_login()
def managed_index():
    active_only = True
    if request.vars.all and request.vars.all == 'yes':
        active_only = False
    props = db(db.user_properties.user == auth.user.email).select().first()    
    if props == None:
        managed_venue_list = []
        managed_user_lists = []
    else:
        managed_venue_list = util.get_list(props.venues_can_manage)
        managed_user_lists = util.get_list(props.managed_user_lists)
    if len(managed_venue_list) > 0:
        if active_only:
            q = (db.venue.id.belongs(managed_venue_list)
                 & (db.venue.is_active == True))
        else:
            q = (db.venue.id.belongs(managed_venue_list))
    else:
        q = (db.venue.id == -1)
    # Admins can see all venues.
    if is_user_admin():
        if active_only:
            q = (db.venue.is_active == True)
        else:
            q = db.venue
    # Deals with search parameter.
    if request.vars.cid and request.vars.cid != '':
        try:
            cid = int(request.vars.cid)
        except ValueError:
            cid = None
        if cid != None and cid in managed_venue_list:
            q = (db.venue.id == cid)
    db.venue.is_approved.readable = True
    links=[
        dict(header=T('Edit'),
             body = lambda r: A(T('Edit'), _class='btn', 
                                _href=URL('venues', 'edit', args=[r.id], user_signature=True))),
        ]
    if is_user_admin():
        db.venue.created_by.readable = True
        db.venue.creation_date.readable = True
        db.venue.is_approved.default = True
        # Useful for debugging.
        db.venue.can_rank_own_submissions.readable = db.venue.can_rank_own_submissions.writable = True
        db.venue.feedback_accessible_immediately.readable = db.venue.feedback_accessible_immediately.writable = True
        fields = [db.venue.name, db.venue.created_by, db.venue.creation_date, db.venue.is_approved, db.venue.is_active]
        links.append(dict(header=T('Delete'),
                          body = lambda r: A(T('Delete'), _class='btn',
                                             _href=URL('venues', 'delete', args=[r.id], user_signature=True))))
    else:
        fields = [db.venue.name, db.venue.creation_date, db.venue.is_approved, db.venue.is_active]
    grid = SQLFORM.grid(q,
        field_id=db.venue.id,
        fields=fields,
        csv=False, details=False,
        create=False, editable=False, deletable=False, 
        links_in_grid=True,
        links=links,
        maxtextlength=24,
        )
    return dict(grid=grid)
    

def set_homework_defaults(bogus):
    """Sets defaults appropriate for most homeworks."""
    db.venue.allow_multiple_submissions.default = False
    # db.venue.allow_multiple_submissions.readable = db.venue.allow_multiple_submissions.writable = False
    db.venue.can_rank_own_submissions.default = False
    # db.venue.can_rank_own_submissions.readable = db.venue.can_rank_own_submissions.writable = False
    db.venue.max_number_outstanding_reviews.default = 1
    db.venue.max_number_outstanding_reviews.readable = db.venue.max_number_outstanding_reviews.writable = False
    db.venue.feedback_is_anonymous.default = True
    db.venue.feedback_is_anonymous.readable = db.venue.feedback_is_anonymous.writable = False
    db.venue.submissions_visible_immediately.default = False
    db.venue.submissions_visible_immediately.readable = db.venue.submissions_visible_immediately.writable = False
    db.venue.feedback_available_to_all.default = False
    db.venue.feedback_available_to_all.readable = db.venue.feedback_available_to_all.writable = False
    db.venue.rating_available_to_all.default = False
    db.venue.rating_available_to_all.readable = db.venue.rating_available_to_all.writable = False
    db.venue.rater_contributions_visible_to_all.default = False
    db.venue.rater_contributions_visible_to_all.readable = db.venue.rater_contributions_visible_to_all.writable = False
    db.venue.latest_grades_date.readable = False
    db.venue.rate_constraint.readable = db.venue.rate_constraint.writable = False
    db.venue.grading_instructions.default = DEFAULT_GRADING_INSTRUCTIONS
    
    
@auth.requires_login()
def edit():
    """Edits or creates a venue."""
    is_edit = (request.args(0) is not None)
    if is_edit:
        c = db.venue(request.args(0))
        if c is None:
            session.flash = T('No such assignment.')
            redirect(URL('venues', 'managed_index'))
    props = db(db.user_properties.user == auth.user.email).select().first()
    if props == None:
        managed_venue_list = []
        managed_user_lists = []
    else:
        managed_venue_list = util.get_list(props.venues_can_manage)
        managed_user_lists = util.get_list(props.managed_user_lists)
    if is_edit and not access.can_manage(c, props):
        session.flash = T('Not authorized.')
        redirect(URL('venues', 'managed_index'))
        
    # Ok, we can edit/create the venue.
    old_managers = []
    old_observers = []
    old_submit_constraint = None
    old_rate_constraint = None
    # These are defaults that can be changed.
    old_can_rank_own_submissions = False
    old_feedback_accessible_immediately = False
    # These are previous values.
    old_name = ''
    old_description = ''
    old_open_date = datetime.utcnow()
    old_close_date = None
    old_rate_open_date = None
    old_rate_close_date = None
    old_submission_instructions = ''
    old_allow_link_submission = False
    old_allow_file_upload = True
    old_is_active = True
    old_is_approved = is_user_admin()
    old_number_of_submissions_per_reviewer = 6
    old_grading_instructions = DEFAULT_GRADING_INSTRUCTIONS
    
    if is_edit:
        title = T('Edit Assignment')
        old_managers = c.managers
        old_observers = c.observers
        old_submit_constraint = c.submit_constraint
        old_rate_constraint = c.rate_constraint
        # Defaults carried for admins only.
        if is_user_admin(): 
            old_can_rank_own_submissions = c.can_rank_own_submissions
            old_feedback_accessible_immediately = c.feedback_accessible_immediately
        # Previous values.
        old_name = c.name
        old_description = keystore_read(c.description)
        old_open_date = c.open_date
        old_close_date = c.close_date
        old_rate_open_date = c.rate_open_date
        old_rate_close_date = c.rate_close_date
        old_submission_instructions = keystore_read(c.submission_instructions)
        old_allow_link_submission = c.allow_link_submission
        old_allow_file_upload = c.allow_file_upload
        old_is_active = c.is_active
        old_is_approved = c.is_approved
        old_number_of_submissions_per_reviewer = c.number_of_submissions_per_reviewer
        old_grading_instructions = keystore_read(c.grading_instructions)
            
    else:
        title = T('Create Assignment')

    # Define list_q as the query defining which user lists the user manages.
    list_q = (db.user_list.id.belongs(managed_user_lists))    

    # Generates the form.
    form = SQLFORM.factory(
        Field('name', default=old_name, comment=T("Name of the assignment (at least 16 characters)."), requires=IS_LENGTH(minsize=16)),
        Field('description', 'text', length=MAX_TEXT_LENGTH, default=old_description, comment=T("Description of the assignment.")),
        Field('managers', 'list:string', requires=[IS_LIST_OF(IS_EMAIL())], default=old_managers, comment=T("Email addresses of assignment managers.")),
        Field('observers', 'list:string', requires=[IS_LIST_OF(IS_EMAIL())], default=old_observers, comment=T("Email addresses of assignment observers (TAs, etc).")),
        Field('submit_constraint', db.user_list, requires = IS_EMPTY_OR(IS_IN_DB(db(list_q), 'user_list.id', '%(name)s', zero=T('-- Nobody --'))), default=old_submit_constraint, label=T('Student list'), comment=T("List of students who can submit and rate.")),
        Field('open_date', 'datetime', default=old_open_date, requires=IS_NOT_EMPTY(), label=T('Submission opening date'), comment=T('In UTC.')),
        Field('close_date', 'datetime', default=old_close_date, requires=IS_NOT_EMPTY(), label=T('Submission deadline'), comment=T('In UTC.')),
        Field('rate_open_date', 'datetime', default=old_rate_open_date, requires=IS_NOT_EMPTY(), label=T('Reviewing start date'), comment=T('In UTC.')),
        Field('rate_close_date', 'datetime', default=old_rate_close_date, requires=IS_NOT_EMPTY(), label=T('Reviewing deadline'), comment=T('In UTC.')),
        Field('submission_instructions', 'text', length=MAX_TEXT_LENGTH, default=old_submission_instructions, comment=T("Write here any special instructions that students should see when submitting.")),
        Field('allow_link_submission', 'boolean', default=old_allow_link_submission, comment=T("Students can also submit a URL.")),
        Field('allow_file_upload', 'boolean', default=old_allow_file_upload, comment=T("Students can include an attachment with their submission.")),
        Field('is_active', 'boolean', default=old_is_active, comment=T('Is the assignment active?')),
        Field('is_approved', 'boolean', default=old_is_approved, comment=T('Has the assignment been approved?'), writable=is_user_admin()),
        Field('number_of_submissions_per_reviewer', 'integer', default=old_number_of_submissions_per_reviewer, requires=IS_INT_IN_RANGE(1,100), comment=T("How many submissions should each student review?")),
        Field('grading_instructions', 'text', length=MAX_TEXT_LENGTH, default=old_grading_instructions),        
        )

    if form.process(onvalidation=validate_venue(True)).accepted:
        # Ok, we can write the data. 
        set_homework_defaults(None)
        if is_edit:
            # First, writes the text fields.
            k_descr = keystore_update(c.description, form.vars.description)
            k_instr = keystore_update(c.submission_instructions, form.vars.submission_instructions)
            k_gradg = keystore_update(c.grading_instructions, form.vars.grading_instructions)
            # Then, updates the venue entry.
            if is_user_admin():
                new_is_approved = form.vars.is_approved
            else:
                new_is_approved = old_is_approved
            c.update_record(
                name = form.vars.name,
                description = k_descr,
                managers = form.vars.managers,
                observers = form.vars.observers,
                submit_constraint = form.vars.submit_constraint,
                rate_constraint = form.vars.rate_constraint,
                open_date = form.vars.open_date,
                close_date = form.vars.close_date,
                rate_open_date = form.vars.rate_open_date,
                rate_close_date = form.vars.rate_close_date,
                submission_instructions = k_instr,
                allow_link_submission = form.vars.allow_link_submission,
                is_approved = new_is_approved,
                number_of_submissions_per_reviewer = form.vars.number_of_submissions_per_reviewer,
                grading_instructions = k_gradg,
                )
            
            # Finally, propagates change due to the change of managers, observers, etc.
            update_venue(c.id, form, old_managers, old_observers, old_submit_constraint, old_rate_constraint)
            id = c.id
            session.flash = T('The assignment has been updated.')
            
        else:
            # We are creating a venue.
            description_id = keystore_write(form.vars.description)
            instructions_id = keystore_write(form.vars.submission_instructions)
            grading_id = keystore_write(form.vars.grading_instructions)
            if is_user_admin():
                new_is_approved = form.vars.is_approved
            else:
                new_is_approved = old_is_approved

            # Creates the new entry.
            id = db.venue.insert(
                name = form.vars.name,
                description = description_id,
                managers = form.vars.managers,
                observers = form.vars.observers,
                submit_constraint = form.vars.submit_constraint,
                rate_constraint = form.vars.rate_constraint,
                open_date = form.vars.open_date,
                close_date = form.vars.close_date,
                rate_open_date = form.vars.rate_open_date,
                rate_close_date = form.vars.rate_close_date,
                submission_instructions = instructions_id,
                allow_link_submission = form.vars.allow_link_submission,
                is_approved = new_is_approved,
                number_of_submissions_per_reviewer = form.vars.number_of_submissions_per_reviewer,
                grading_instructions = grading_id,
                )
            
            # Propagates the updates to the permission lists.
            create_venue(id, form)
            session.flash = T('The assignment has been created.')
            
        # Sends the user to look at the newly created or updated venue.
        db.commit()
        redirect(URL('venues', 'view_venue', args=[id]))
        
    return dict(form=form, title=title)
            
            
@auth.requires_signature()
def delete():
    c = db.venue(request.args(0))
    if c is None:
        session.flash = T('No such assignment.')
        redirect(URL('venues', 'managed_index'))
    description_record = keystore_read(c.description)
    grading_instructions_record = keystore_read(c.grading_instructions)
    submission_instructions_record = keystore_read(c.submission_instructions)
    props = db(db.user_properties.user == auth.user.email).select().first()
    if props == None:
        managed_venue_list = []
        managed_user_lists = []
    else:
        managed_venue_list = util.get_list(props.venues_can_manage)
        managed_user_lists = util.get_list(props.managed_user_lists)
    # Deletes the text records.
    keystore_delete(c.description)
    keystore_delete(c.submission_instructions)
    keystore_delete(c.grading_instructions)
    # Pre-propagates the deletion
    delete_venue(c.id)
    # Finally, really deletes it.
    db(db.venue.id == c.id).delete()
    db.commit()
    session.flash = T('The assignment has been deleted')
    redirect(URL('venues', 'managed_index'))


def get_list_members(user_list_id):
    """Utility function to get members of user list."""
    user_list = db.user_list(user_list_id)
    if user_list is None:
        return []
    return util.get_list(user_list.user_list)

    
def validate_venue(raters_equal_submitters):
    def f(form):
        """Validates the form venue, splitting managers listed on the same line."""
        form.vars.managers = util.normalize_email_list(form.vars.managers)
        form.vars.observers = util.normalize_email_list(form.vars.observers)
        if raters_equal_submitters:
            form.vars.rate_constraint = form.vars.submit_constraint
        if auth.user.email not in form.vars.managers:
            form.vars.managers = [auth.user.email] + form.vars.managers
        # Checks the dates.
        if form.vars.close_date < form.vars.open_date:
            form.errors.close_date = T('The submission deadline must follow the submission opening date.')
        if form.vars.rate_open_date < form.vars.close_date:
            form.errors.rate_open_date = T('The review start date must follow the submission deadline.')
        if form.vars.rate_close_date < form.vars.rate_open_date:
            form.errors.rate_close_date = T('The review deadline must follow the review starting date.')
    return f

def add_venue_to_user_managers(id, user_list):
    for m in user_list:
        u = db(db.user_properties.user == m).select(db.user_properties.venues_can_manage).first()
        if u == None:
            # We never heard of this user, but we still create the permission.
            db.user_properties.insert(user=m, venues_can_manage = [id])
        else:
            l = u.venues_can_manage
            l = util.list_append_unique(l, id)
            db(db.user_properties.user == m).update(venues_can_manage = l)
    db.commit()
        
def add_venue_to_user_observers(id, user_list):
    for m in user_list:
        u = db(db.user_properties.user == m).select(db.user_properties.venues_can_observe).first()
        if u == None:
            # We never heard of this user, but we still create the permission.
            db.user_properties.insert(user=m, venues_can_observe = [id])
        else:
            l = u.venues_can_observe
            l = util.list_append_unique(l, id)
            db(db.user_properties.user == m).update(venues_can_observe = l)
    db.commit()
        
def add_venue_to_user_submit(id, user_list):
    for m in user_list:
        u = db(db.user_properties.user == m).select(db.user_properties.venues_can_submit).first()
        if u == None:
            # We never heard of this user, but we still create the permission.
            db.user_properties.insert(user=m, venues_can_submit = [id])
        else:
            l = u.venues_can_submit
            l = util.list_append_unique(l, id)
            db(db.user_properties.user == m).update(venues_can_submit = l)
    db.commit()
        
def add_venue_to_user_rate(id, user_list):
    for m in user_list:
        u = db(db.user_properties.user == m).select(db.user_properties.venues_can_rate).first()
        if u == None:
            # We never heard of this user, but we still create the permission.
            db.user_properties.insert(user=m, venues_can_rate = [id])
        else:
            l = u.venues_can_rate
            l = util.list_append_unique(l, id)
            db(db.user_properties.user == m).update(venues_can_rate = l)
    db.commit()

def delete_venue_from_managers(id, user_list):
    for m in user_list:
        u = db(db.user_properties.user == m).select(db.user_properties.venues_can_manage).first()
        if u != None:
            l = util.list_remove(u.venues_can_manage, id)
            db(db.user_properties.user == m).update(venues_can_manage = l)
    db.commit()
       
def delete_venue_from_observers(id, user_list):
    for m in user_list:
        u = db(db.user_properties.user == m).select(db.user_properties.venues_can_observe).first()
        if u != None:
            l = util.list_remove(u.venues_can_observe, id)
            db(db.user_properties.user == m).update(venues_can_observe = l)
    db.commit()
       
def delete_venue_from_submitters(id, user_list):
    for m in user_list:
        u = db(db.user_properties.user == m).select(db.user_properties.venues_can_submit).first()
        if u != None:
            l = util.list_remove(u.venues_can_submit, id)
            db(db.user_properties.user == m).update(venues_can_submit = l)
    db.commit()
       
def delete_venue_from_raters(id, user_list):
    for m in user_list:
        u = db(db.user_properties.user == m).select(db.user_properties.venues_can_rate).first()
        if u != None:
            l = util.list_remove(u.venues_can_rate, id)
            db(db.user_properties.user == m).update(venues_can_rate = l)
    db.commit()
                        
def create_venue(id, form):
    """Processes the creation of a context, propagating the effects."""
    # First, we need to add the context for the new managers.
    add_venue_to_user_managers(id, form.vars.managers)
    add_venue_to_user_observers(id, form.vars.observers)
    # If there is a submit constraint, we need to allow all the users
    # in the list to submit.
    if not util.is_none(form.vars.submit_constraint):
        # We need to add everybody in that list to submit.
        add_venue_to_user_submit(id, get_list_members(form.vars.submit_constraint))
    # If there is a rating constraint, we need to allow all the users
    # in the list to rate.
    if not util.is_none(form.vars.rate_constraint):
        add_venue_to_user_rate(id, get_list_members(form.vars.rate_constraint))
    # Authorizationi message.
    if not is_user_admin():
        session.flash = T('Your assignment has been created. '
                          'Before it can be used, it needs to be approved.')
        
                
def update_venue(id, form, old_managers, old_observers, old_submit_constraint, old_rate_constraint):
    """A venue is being updated.  We need to return a callback for the form,
    that will produce the proper update, taking into account the change in permissions."""
    # Managers.
    add_venue_to_user_managers(id, util.list_diff(form.vars.managers, old_managers))
    delete_venue_from_managers(id, util.list_diff(old_managers, form.vars.managers))
    # Observers.
    add_venue_to_user_observers(id, util.list_diff(form.vars.observers, old_observers))
    delete_venue_from_observers(id, util.list_diff(old_observers, form.vars.observers))
    # Submitters.
    if str(old_submit_constraint) != str(form.vars.submit_constraint):
        # We need to update.
        if old_submit_constraint != None:
            delete_venue_from_submitters(id, get_list_members(old_submit_constraint))
        if not util.is_none(form.vars.submit_constraint):
            user_list = get_list_members(form.vars.submit_constraint)
            add_venue_to_user_submit(id, user_list)
    # Raters.
    if str(old_rate_constraint) != str(form.vars.rate_constraint):
        # We need to update.
        if old_rate_constraint != None:
            delete_venue_from_raters(id, get_list_members(old_rate_constraint))
        if not util.is_none(form.vars.rate_constraint):
            add_venue_to_user_rate(id, get_list_members(form.vars.rate_constraint))
                
def delete_venue(id):
    c = db.venue[id]
    delete_venue_from_managers(id, c.managers)
    delete_venue_from_observers(id, c.observers)
    if c.submit_constraint != None:
        user_list = get_list_members(c.submit_constraint)
        delete_venue_from_submitters(id, user_list)
    if c.rate_constraint != None:
        user_list = get_list_members(c.rate_constraint)
        delete_venue_from_raters(id, user_list)
